/** @file res_texture_mgr.cpp
	@brief Texture manager */

#include "res/res_texture_mgr.h"

using namespace G3;

G3::TEXTURE_MANAGER G3::TextureManager;

//==============================================================================
// Constructor
//==============================================================================
G3::TEXTURE_MANAGER::TEXTURE_MANAGER () {
    build_manually = false;
    used_filter = GTF_MIPMAP;
    lock_next_texture_bitmap = false;
}

//==============================================================================
// Destructor
//==============================================================================
G3::TEXTURE_MANAGER::~TEXTURE_MANAGER () {
}

//==============================================================================
// Locks or unlocks the next loaded texture's bitmap for user access
//	If locked, the bitmap data is safe from being deleted
//	If unlocked, the bitmap will be deleted immediately after building the texture
//==============================================================================
void G3::TEXTURE_MANAGER::LockTextureBitmap () {
    lock_next_texture_bitmap = true;
}

void G3::TEXTURE_MANAGER::UnLockTextureBitmap () {
    lock_next_texture_bitmap = false;
}

bool G3::TEXTURE_MANAGER::IsTextureBitmapLocked () {
    return lock_next_texture_bitmap;
}

//==============================================================================
// Gets the texture handle by filename
//
// And loads the texture if needed
//==============================================================================
H_TEXTURE G3::TEXTURE_MANAGER::GetHandle (const std::string &filename) {
    if (filename.empty ()) {
        if (log)
            log->Report ("TEXTURE_MANAGER::GetTexture: Filename argument is an empty string\n");
        return 0;
    }

    std::pair <std::map<std::string, H_TEXTURE>::iterator, bool> nipair;
    boost::shared_ptr<TEXTURE> pTex;
    STREAM *pStream = NULL;
    std::string fPath;

    // Try to insert this new texture
    nipair = name_index.insert (std::pair<std::string, H_TEXTURE> (filename, H_TEXTURE ()));

    // Success?
    if (nipair.second) {
        // Acquire a new texture
        pTex = handle_mgr.Acquire (nipair.first->second);

        // Then load it in
        if (pTex) {
            pTex->BitmapLocked = lock_next_texture_bitmap;

            if (filename != "G3::Blank") {
                // Get the filepath
                fPath = MainFilesys.GetPath (filename.c_str ());

                if (fPath.size () > 1) {
                    // Read the whole file (assume that it is not too big)
                    pStream = MainFileHandler.ReadWholeFile (fPath.c_str ());

                    if (pStream) {
                        if (log)
                            log->Report ("Loading image \"%s\"\n", fPath.c_str ());
                        // Try to load this image
                        if (!pTex->LoadImage (pStream, fPath)) {
                            if (log)
                                log->Report ("Failed to load the image..\n");

                            Delete (nipair.first->second);
                            nipair.first->second = H_TEXTURE ();
                        } else {
                            // Build it aswell in case this behaviour is not overridden
                            if (!build_manually)
                                BuildTexture2D (nipair.first->second, used_filter);
                        }

                        delete pStream;
                    } else {
                        if (log)
                            log->Report ("ERROR! TEXTURE_MANAGER::GetTexture: Could not read the texture file \"%s\"..\n", filename.c_str ());
                    }
                } else {
                    if (log)
                        log->Report ("ERROR! TEXTURE_MANAGER::GetTexture: Could not find the texture file \"%s\"..\n", filename.c_str ());
                }
            } else {
                // Someone needs just an empty texture handle?
            }
        } else {
            if (log)
                log->Report ("ERROR! TEXTURE_MANAGER::GetTexture: Could not acquire memory for the texture\n");
        }
    }
    // Return the handle either to the new texture or
    // to one with the same name
    return nipair.first->second;
}

//==============================================================================
// Builds the texture
//==============================================================================
int G3::TEXTURE_MANAGER::BuildTexture2D (H_TEXTURE handle, TEXTURE_FILTER filter) {
    if (handle.IsNull())
        return -1;

    boost::shared_ptr<TEXTURE> pTex = GetPointer (handle);

    if (pTex == boost::shared_ptr<TEXTURE> ())
        return -1;

    return pTex->glBuild2D (filter);
}

//==============================================================================
// Gets the dimensions of a texture
//==============================================================================
VEC2I G3::TEXTURE_MANAGER::GetTextureDimensions (H_TEXTURE handle) {
    if (handle.IsNull())
        return VEC2I (0, 0);

    boost::shared_ptr<TEXTURE> pTex = GetPointer (handle);

    if (!pTex)
        return VEC2I (0, 0);

    return VEC2I (pTex->GetWidth (), pTex->GetHeight ());
}

//==============================================================================
// Gets the bitmap of a recently loaded texture
//
//	NOTE:: The texture manager has the right to free texture bitmaps
//==============================================================================
boost::shared_ptr<G3::BITMAP> G3::TEXTURE_MANAGER::GetTextureBitmap (H_TEXTURE handle) {
    if (handle.IsNull())
        return boost::shared_ptr<G3::BITMAP>();

    boost::shared_ptr<TEXTURE> pTex = GetPointer (handle);

    if (!pTex)
        return boost::shared_ptr<G3::BITMAP> ();

    return pTex->Bitmap;
}

//==============================================================================
// Gets the identifier (Specific to the renderer) of the texture
//==============================================================================
uint G3::TEXTURE_MANAGER::GetTextureID (H_TEXTURE handle) {
    if (handle.IsNull())
        return 0;

    boost::shared_ptr<TEXTURE> pTex = GetPointer (handle);

    if (!pTex)
        return 0;

    return pTex->ID;
}

//==============================================================================
// Gets the texture flags
//==============================================================================
uint G3::TEXTURE_MANAGER::GetTextureFlags (H_TEXTURE handle) {
    if (handle.IsNull())
        return 0;

    boost::shared_ptr<TEXTURE> pTex = GetPointer (handle);

    if (!pTex)
        return 0;

    return pTex->GetFlags ();
}

//==============================================================================
// Applies the texture
//==============================================================================
void G3::TEXTURE_MANAGER::Apply (H_TEXTURE handle) {
    if (handle.IsNull())
        return;

    boost::shared_ptr<TEXTURE> pTex = GetPointer (handle);

    Assert (pTex != boost::shared_ptr<TEXTURE>(), "TEXTURE_MANAGER::Apply: Not a valid texture handle");

    glBindTexture (GL_TEXTURE_2D, pTex->ID);
}

//==============================================================================
// Gets and applies the texture
//==============================================================================
void G3::TEXTURE_MANAGER::Apply (const char *Filename) {
    Assert (Filename, "TEXTURE_MANAGER::Apply: Filename argument is a NULL pointer");

    H_TEXTURE handle = GetHandle (Filename);

    if (handle.IsNull ())
        return;

    boost::shared_ptr<TEXTURE> pTex = GetPointer (handle);

    Assert (pTex, "TEXTURE_MANAGER::Apply: Not a valid texture handle");

    glBindTexture (GL_TEXTURE_2D, pTex->ID);
}

void G3::TEXTURE_MANAGER::Delete (H_TEXTURE handle) {
    if (handle.IsNull())
        return;

    std::map<std::string, H_TEXTURE>::iterator it;
    boost::shared_ptr<TEXTURE> p = handle_mgr.GetData (handle);

    it = name_index.end ();

    if (p) {
        it = name_index.find (p->Name);

        if (it != name_index.end ())
            name_index.erase (it);

        p->Clear ();

        handle_mgr.Release (handle);
    }
}

//==============================================================================
// Dumps the whole texture manager into the log
//==============================================================================
void G3::TEXTURE_MANAGER::Dump () {
    std::map<std::string, H_TEXTURE>::iterator it;

    if (log) {
        log->Report ("Dumping NameIndex:\n");

        if (!name_index.empty ()) {
            it = name_index.begin ();
            while (it != name_index.end ()) {
                log->Report (" \"%s\" -> 0x%X\n", it->first.c_str (), (uint) it->second);

                it++;
            }
        } else {
            log->Report ("<Empty>\n");
        }
    }

    handle_mgr.Dump ();
}

void G3::TEXTURE_MANAGER::Clear () {
    std::map<std::string, H_TEXTURE>::iterator it;
    boost::shared_ptr<TEXTURE> pTex;
    H_TEXTURE handle;
    // Loop through the NameIndex
    it = name_index.begin ();
    while (it != name_index.end ()) {
        handle = it->second;
        if (!handle.IsNull ()) {
            pTex = handle_mgr.GetData (handle);
            // And clear all resources
            if (pTex)
                pTex->Clear ();
        }

        it++;
    }
}
